package apistore

import (
	"alarmservice/com"
	"container/list"
	"errors"
	"sync"
	"time"
)

type Token struct {
	Uid        int64  `json:"uid"`
	Value      string `json:"token"`
	Key        string `json:"key"`
	Expires    int64  `json:"expires"`
	ExpiresIn  int64  `json:"expires_in"`
	ServerTime int64  `json:"server_time"`
}

var tokenMap map[string]*Token
var tokenList *list.List
var tl sync.RWMutex

func init() {
	tokenMap = make(map[string]*Token)
	tokenList = list.New()
	callTokenGc()
}

func callTokenGc() {
	TokenGc()
	time.AfterFunc(time.Duration(3600)*time.Second, func() { callTokenGc() })
}

func NewToken(uid int64) *Token {
	t := new(Token)
	for {
		t.Value = string(com.RandomCreateBytes(32))
		_, ok := tokenMap[t.Value]
		if !ok {
			break
		}
	}

	t.Uid = uid
	t.Key = string(com.RandomCreateBytes(32))

	//强制一天时间
	t.ExpiresIn = 86400
	t.ServerTime = time.Now().Unix()
	t.Expires = t.ServerTime + t.ExpiresIn

	defer tl.Unlock()
	tl.Lock()
	tokenMap[t.Value] = t
	_ = tokenList.PushBack(t)

	return t
}

func GetToken(val string) (*Token, error) {
	if val == "" {
		return nil, errors.New("empty val")
	}

	t, ok := tokenMap[val]
	if !ok {
		return nil, errors.New("token not exist")
	}

	return t, nil
}

func TokenGc() {
	tl.RLock()
	for {
		element := tokenList.Front()
		if element == nil {
			break
		}

		t := element.Value.(*Token)

		if t.Expires < time.Now().Unix() {
			tl.RUnlock()
			tl.Lock()
			tokenList.Remove(element)
			delete(tokenMap, t.Value)
			tl.Unlock()
			tl.RLock()
		} else {
			break
		}
	}
	tl.RUnlock()
}
